unit SkypeControl;

interface

uses
  Windows, Messages, SysUtils, Classes, Forms, SkypeControlConst;

type
  TSkypeControl = class(TComponent)
  private
    //--------------------------------------------------------------------------
    //-- private variables -----------------------------------------------------

    FHooked       : boolean;
    MsgIDAttach   : cardinal;
    MsgIDDiscover : cardinal;

    SkypeAPIWindowHandle : HWND;

    RequestPending : boolean; // set to true, if a bl* method is waiting
                              // for a event
    RequestID      : string;  // helps the event-handler (evaluate*) to
                              // determine if the actual event is the requested
                              // one
    RequestResult  : Variant; // place for the event-handlers to put the
                              // received information
    RequestError   : boolean; // if an error occurs while a request was pending
                              // the request will be aborted. RequestError
                              // provides this information to the waiting bl*
                              // method

    //--------------------------------------------------------------------------
    //-- private representations of properties ---------------------------------

    FEventOnRequest : boolean;
    FAttached       : boolean;
    FIdentName      : string;
    FAutoReAttach   : boolean;
    FUsedProtocol   : integer;

    //--------------------------------------------------------------------------
    //-- private representations of events -------------------------------------

    FOnAttachSuccess      : TNotifyEvent;
    FOnAttachPending      : TNotifyEvent;
    FOnAttachRefused      : TNotifyEvent;
    FOnAPINotAvailable    : TNotifyEvent;
    FOnAPIAvailable       : TNotifyEvent;
    FOnError              : TSkypeErrorNotify;
    FOnUserStatus         : TSkypeUserStatusNotify;
    FOnUserInfo           : TSkypeUserNotify;
    FOnName               : TSkypeNameNotify;
    FOnProtocol           : TSkypeProtocolNotify;
    FOnPong               : TNotifyEvent;
    FOnCallInfo           : TSkypeCallNotify;
    FOnMessageInfo        : TSkypeMessageNotify;
    FOnConnstatus         : TSkypeConnStatusNotify;
    FOnCurrentUserHandle  : TSkypeCurrentUserHandleNotify;
    FOnSearchResult       : TSkypeSearchNotify;
    FOnCallHistoryChanged : TNotifyEvent;
    FOnIMHistoryChanged   : TNotifyEvent;
    FOnAudioInInfo        : TSkypeAudioNotify;
    FOnAudioOutInfo       : TSkypeAudioNotify;
    FOnMuteInfo           : TSkypeMuteNotify;
    FOnPrivilegeInfo      : TSkypePrivilegeNotify;
    FOnProfileInfo        : TSkypeProfileNotify;
    FOnVersionInfo        : TSkypeVersionNotify;
    FOnChatInfo           : TSkypeChatNotify;
    FOnChatMsgInfo        : TSkypeChatMsgNotify;
    FOnNotSupportedError  : TNotSupportedErrorNotify;
    FOnCopyData           : TSkypeCopyData;

    //--------------------------------------------------------------------------
    //-- private methods -------------------------------------------------------

    // string parsing function
    function getFirstDelimited(var aString : string;
                               Delimiters  : TSysCharSet) : string;

    // Message hook which is inserted at the beginning of the application's
    // message loop
    function MessageHook(var Msg: TMessage): Boolean;

    // Sending NAME and PROTOCOL to Skype
    procedure introduce;



    // Helper-function to receive WM_COPYDATA Messages
    // returns the received data as a List of strings
    function ReceiveData(aMsg : TMessage; var subdata : string) : string;

    // Dispatch evaluates the first Parameter of a received message and calls
    // the according method to further process the message
    procedure DispatchMessage(Key, subdata : string);

    // each method processes one kind of message
    procedure EvaluateERROR(subdata : string);
    procedure EvaluateUSERSTATUS(subdata : string);
    procedure EvaluateUSER(subdata : string);
    procedure EvaluateNAME(subdata : string);
    procedure EvaluatePROTOCOL(subdata : string);
    procedure EvaluatePONG(subdata : string);
    procedure EvaluateCALL(subdata : string);
    procedure EvaluateMESSAGE(subdata : string);
    procedure EvaluateCONNSTATUS(subdata : string);
    procedure EvaluateCURRENTUSERHANDLE(subdata : string);
    procedure EvaluateSEARCHRESULTS(aType : TSkypeSearchType; subdata : string);
    procedure EvaluateCALLHISTORYCHANGED(subdata : string);
    procedure EvaluateIMHISTORYCHANGED(subdata : string);
    procedure EvaluateAUDIO_IN(subdata : string);
    procedure EvaluateAUDIO_OUT(subdata : string);
    procedure EvaluateMUTE(subdata : string);
    procedure EvaluatePRIVILEGE(subdata : string);
    procedure EvaluatePROFILE(subdata : string);
    procedure EvaluateSKYPEVERSION(subdata : string);
    procedure EvaluateCHAT(subdata : string);
    procedure EvaluateCHATMESSAGE(subdata : string);

    // method to reset the requestvariables
    procedure ResetRequest;

    // method to trigger the OnNotSupportedError event
    // killrequest parameter is used to stop possible pending request
    // e.g. in the GetProperty methods
    procedure NotSupportedError(errormsg    : string;
                                killrequest : boolean = false);

  protected
    { Protected declarations }
  public
    //--------------------------------------------------------------------------
    //-- constructor / destructor ----------------------------------------------
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    //--------------------------------------------------------------------------
    //-- public methods --------------------------------------------------------

    // Helper-function to send WM_COPYDATA Messages
    // returns true if successfull (Data was sent or aString was empty)
    function SendData(aString : string) : boolean;

    // Try to connect to Skype
    procedure AttachToSkype(Protocol : integer = SUPPORTED_PROTOCOL);

    // Disconnect from Skype
    procedure DetachFromSkype;

    // the following methods just send messages to Skype. The results
    // have to be gathered manually with the events generated by
    // the Skype-messages.

    // Search requests a specific type of information about the target.
    // If no target is specified, then all results are returned.
    // aType specifies the information type:
    // stUsers: TARGET - username or a part of it. If the search string contains
    //          "@", then search is performed by e-mail address (note that
    //          e-mail address has to match 100%).
    // stFriends: TARGET not allowed
    // stCalls, stActiveCalls, stMissedCalls, stMessages, stMissedMessages :
    //   TARGET optional
    procedure Search(aType : TSkypeSearchType; Target : string = '');

    // these methods request one property at a time...
    procedure GetProperty(Username        : string;
                          UserProperty    : TSkypeUserProperty);    overload;

    procedure GetProperty(CallID          : integer;
                          CallProperty    : TSkypeCallProperty);    overload;

    procedure GetProperty(MessageID       : integer;
                          MessageProperty : TSkypeMessageProperty); overload;

    procedure GetProperty(Chatname        : string;
                          ChatProperty    : TSkypeChatProperty);    overload;

    procedure GetProperty(ChatMsgID       : integer;
                          ChatMsgProperty : TSkypeChatMsgProperty); overload;

    procedure GetUserStatus;
    procedure GetConnStatus;
    procedure GetAudioInInfo;
    procedure GetAudioOutInfo;
    procedure GetCurrentUserHandle;
    procedure GetMuteInfo;
    procedure GetPrivilegeInfo(priv : TSkypePrivilege);
    procedure GetProfileInfo(prop : TSkypeProfile);
    procedure GetSkypeVersion;

    procedure SetProperty(CallID : integer;
                          CallProperty : TSkypeCallProperty;
                          Value : string = ''); overload;

    procedure SetProperty(MessageID : integer;
                          MessageProperty : TSkypeMessageProperty); overload;

    procedure SetProperty(ChatMsgID : integer;
                          ChatMsgProperty : TSkypeChatMsgProperty); overload;

    procedure SetUserStatus(Status : TSkypeUserStatus);
    procedure SetMute(Muted : boolean);

    procedure Call(Target : string); //Target can be a username or phone number
                                     // or speed dial number...

    procedure SendMsg(Username : string; aText : string);


    // the following methods can open some dialogs of Skype
    procedure AddContactDialog; // Opens "Add a contact"
    procedure IMDialog(username : string; aText : string); // Opens
                                                           // "Instant Message"
    procedure OpenChat(users : string); // Opens a chat window with users
                                     // example for users "heinz, harry, hennes"
    procedure FocusSkype;

    // a method to test the connection to Skype. If successful a OnPong is
    // triggered
    procedure PingSkype;


    // the following methods gather information from Skype automatically. They
    // return not until the response message(s) from Skype is(are) received.
    function blSearch(aType : TSkypeSearchType;
                      Target : string = '') : TStringList;

    function blGetProperty(Username        : string;
                           UserProperty    : TSkypeUserProperty)    : string;
                                                                       overload;

    function blGetProperty(CallID          : integer;
                           CallProperty    : TSkypeCallProperty)    : string;
                                                                       overload;

    function blGetProperty(MessageID       : integer;
                           MessageProperty : TSkypeMessageProperty) : string;
                                                                       overload;

    function blGetProperty(Chatname        : string;
                           ChatProperty    : TSkypeChatProperty)    : string;
                                                                       overload;

    function blGetProperty(ChatMsgID       : integer;
                           ChatMsgProperty : TSkypeChatMsgProperty) : string;
                                                                       overload;

    function blGetUserStatus : TSkypeUserStatus;
    function blGetConnStatus : TSkypeConnStatus;
    function blGetAudioInInfo  : string;
    function blGetAudioOutInfo : string;
    function blGetCurrentUserHandle : string;
    function blGetMuteInfo : boolean;
    function blGetPrivilegeInfo(priv : TSkypePrivilege) : boolean;
    function blGetProfileInfo(prop : TSkypeProfile) : string;
    function blGetSkypeVersion : string;

    // this function does not block during the hole call, it just returns the
    // callid of the starting call...
    function blCall(Target : string) : integer;

    //--------------------------------------------------------------------------
    //-- public Properties -----------------------------------------------------

    // if set to true, the answers of Skype to the bl* methods will generate
    // events...
    property EventOnRequest : boolean read  FEventOnRequest
                                      write FEventOnRequest;

    // set to true, if shortly before OnAttachSuccess Event
    property Attached : boolean read FAttached;

    // information about the used protocol, protocol is set by attach method
    property UsedProtocol : integer read FUsedProtocol;

  published
    //--------------------------------------------------------------------------
    //-- published Properties --------------------------------------------------

    // This Name is used to identifiy the Client towards Skype
    property IdentName : string read FIdentName write FIdentName;

    // If set to true, then an attach-attempt is made on the
    // OnAPIAvailable event...
    property AutoReAttach : boolean read FAutoReAttach write FAutoReAttach;

    //--------------------------------------------------------------------------
    //-- published Events ------------------------------------------------------

    // Client is successfully attached
    property OnAttachSuccess : TNotifyEvent read  FOnAttachSuccess
                                            write FOnAttachSuccess;

    // Skype has acknowledged connection request and is waiting for confirmation
    // from the user.
    // The client is not yet attached and should wait for OnAttachSuccess.
    property OnAttachPending : TNotifyEvent read  FOnAttachPending
                                            write FOnAttachPending;

    // User has explicitly denied access to client
    property OnAttachRefused : TNotifyEvent read  FOnAttachRefused
                                            write FOnAttachRefused;

    // API is not available at the moment. For example, this happens when no
    // user is currently logged in. Client should wait for OnAPIAvailable before
    // making any further connection attempts.
    property OnAPINotAvailable : TNotifyEvent read  FOnAPINotAvailable
                                              write FOnAPINotAvailable;
    property OnAPIAvailable    : TNotifyEvent read  FOnAPIAvailable
                                              write FOnAPIAvailable;

    // The error response is sent by Skype each time Skype senses an error
    // condition, which includes syntactically incorrect commands, internal
    // inconsistencies etc.
    // Parameter "Code" is a number that uniquely identifies error condition and
    // Parameter "Description" is a optional brief description of the situation,
    // given in English.
    property OnError : TSkypeErrorNotify read FOnError write FOnError;

    // if Skype sends a USERSTATUS Messages (e.g. USERSTATUS AWAY) the event is
    // triggered.
    property OnUserStatus : TSkypeUserStatusNotify read  FOnUserStatus
                                                   write FOnUserStatus;

    // Notifications are sent by Skype either if the corresponding object
    // changes, or if the value of the property is asked with GET command. Also,
    // if the property is changed by SET command, the change is confirmed with a
    // notification. The OnUserInfo event is triggered, when a notification
    // about user object properties is received.
    property OnUserInfo : TSkypeUserNotify read FOnUserInfo write FOnUserInfo;

    // the event is triggered if NAME message is received, usually to confirm
    // the NAME command.
    property OnName : TSkypeNameNotify read FOnName write FOnName;

    // Whenever a new version of the API is released the protocol version number
    // is increased. When a client starts using the API then the client must
    // tell the Skype API the latest protocol version that it supports. Skype
    // will reply with its latest version number and the number reported by
    // Skype will be the protocol version used. Skype will never reply with a
    // protocol version which is newer than the version the client application
    // supports. Skype defaults to protocol version 1.
    // Skype-supported version can be queried with PROTOCOL 99999.
    // Example: Client speaks version 3 and tells Skype "PROTOCOL 3", Skype
    // knows version 2 and replies with "PROTOCOL 2". Version 2 will be the used
    // protocol in this case.
    property OnProtocol : TSkypeProtocolNotify read  FOnProtocol
                                               write FOnProtocol;

    // event is triggered if PONG message is received. Skype sends the PONG
    // message to confirm the PING command
    property OnPong : TNotifyEvent read FOnPong write FOnPong;

    // The OnCallInfo event is triggered, when a notification about call object
    // properties is received.
    property OnCallInfo : TSkypeCallNotify read FOnCallInfo write FOnCallInfo;

    // The OnMessageInfo event is triggered, when a notification about message
    // object properties is received.
    property OnMessageInfo : TSkypeMessageNotify read  FOnMessageInfo
                                                 write FOnMessageInfo;

    // if Skype sends a CONNSTATUS Message (e.g. CONNSTATUS ONLINE) the event is
    // triggered.
    property OnConnStatus : TSkypeConnStatusNotify read  FOnConnStatus
                                                   write FOnConnStatus;

    // event is triggered if CURRENTUSERHANDLE Message is received
    property OnCurrentUserHandle : TSkypeCurrentUserHandleNotify
                                                     read  FOnCurrentUserHandle
                                                     write FOnCurrentUserHandle;

    // event is triggered if result of the SEARCH command is received
    property OnSearchResult : TSkypeSearchNotify read  FOnSearchResult
                                                 write FOnSearchResult;

    // Notifies about call history being changed and that it needs to be
    // reloaded. Right now it only happens, when all of the call history has
    // been deleted.
    property OnCallHistoryChanged : TNotifyEvent read  FOnCallHistoryChanged
                                                 write FOnCallHistoryChanged;

    // Notifies about instant message history being changed and that it needs to
    // be reloaded. Right now it only happens, when IM history for the specific
    // user deleted.
    property OnIMHistoryChanged : TNotifyEvent read  FOnIMHistoryChanged
                                               write FOnIMHistoryChanged;

    // event is triggered if confirmation for SET AUDIO_IN command or
    // result of GET AUDIO_IN command is received.
    // Empty device name means "default device".
    property OnAudioInInfo : TSkypeAudioNotify read  FOnAudioInInfo
                                               write FOnAudioInInfo;

    // event is triggered if confirmation for SET AUDIO_OUT command or
    // result of GET AUDIO_OUT command is received.
    // Empty device name means "default device".
    property OnAudioOutInfo : TSkypeAudioNotify read  FOnAudioOutInfo
                                                write FOnAudioOutInfo;

    // event is triggered if confirmation for SET MUTE command or
    // result of GET MUTE command is received.
    property OnMuteInfo : TSkypeMuteNotify read FOnMuteInfo write FOnMuteInfo;

    // event is triggered if result of GET PRIVILEGE command is received.
    property OnPrivilegeInfo : TSkypePrivilegeNotify read  FOnPrivilegeInfo
                                                     write FOnPrivilegeInfo;

    // event is triggered if result of GET PROFILE command is received.
    property OnProfileInfo : TSkypeProfileNotify read  FOnProfileInfo
                                                 write FOnProfileInfo;

    // event is triggered if result of GET SKYPEVERSION command is received.
    property OnVersionInfo : TSkypeVersionNotify read  FOnVersionInfo
                                                 write FOnVersionInfo;

    // The OnChatInfo event is triggered, when a notification about chat
    // object properties is received.
    property OnChatInfo : TSkypeChatNotify read  FOnChatInfo
                                           write FOnChatInfo;

    // The OnChatMsgInfo event is triggered, when a notification about chatmsg
    // object properties is received.
    property OnChatMsgInfo : TSkypeChatMsgNotify read  FOnChatMsgInfo
                                                 write FOnChatMsgInfo;

    // the OnNotSupportedError event is triggered when a received message or
    // a executed method tries to do something not supported, e.g. setting
    // userstatus to usSkypeMe with protocol 1
    property OnNotSupportedError : TNotSupportedErrorNotify
                                     read  FOnNotSupportedError
                                     write FOnNotSupportedError;

    // event is triggered each time a WM_COPYDATA message from Skype is received
    property OnCopyData : TSkypeCopyData read FOnCopyData write FOnCopyData;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('System', [TSkypeControl]);
end;

constructor TSkypeControl.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  // init internals...
  FHooked              := false;
  MsgIDAttach          := 0;
  MsgIDDiscover        := 0;
  SkypeAPIWindowHandle := 0;

  FUsedProtocol        := -1;
  FEventOnRequest      := false;
  FAttached            := false;
  FIdentName           := 'Delphi App.';
  FAutoReAttach        := false;

  FOnAttachSuccess      := nil;
  FOnAttachPending      := nil;
  FOnAttachRefused      := nil;
  FOnAPINotAvailable    := nil;
  FOnAPIAvailable       := nil;
  FOnError              := nil;
  FOnUserStatus         := nil;
  FOnUserInfo           := nil;
  FOnName               := nil;
  FOnProtocol           := nil;
  FOnPong               := nil;
  FOnCallInfo           := nil;
  FOnMessageInfo        := nil;
  FOnConnstatus         := nil;
  FOnCurrentUserHandle  := nil;
  FOnSearchResult       := nil;
  FOnCallHistoryChanged := nil;
  FOnIMHistoryChanged   := nil;
  FOnAudioInInfo        := nil;
  FOnAudioOutInfo       := nil;
  FOnMuteInfo           := nil;
  FOnPrivilegeInfo      := nil;
  FOnProfileInfo        := nil;
  FOnVersionInfo        := nil;
  FOnChatInfo           := nil;
  FOnChatMsgInfo        := nil;
  FOnNotSupportedError  := nil;
  FOnCopyData           := nil;

  ResetRequest;

  // if app is running, register messages and hook into the
  // application's message loop
  if not (csDesigning in ComponentState)
    then begin
    	MsgIDAttach   := RegisterWindowMessage('SkypeControlAPIAttach');
	    MsgIDDiscover := RegisterWindowMessage('SkypeControlAPIDiscover');
      if (MsgIDAttach <> 0) and (MsgIDDiscover <> 0)
        then Begin
          Application.HookMainWindow(MessageHook);
          FHooked := True;
        end;
    end;
end;

destructor TSkypeControl.Destroy;
begin
  // release the message hook if needed
  if FHooked
    then Application.UnhookMainWindow(MessageHook);
  inherited Destroy;
end;

function TSkypeControl.getFirstDelimited(var aString : string;
                                         Delimiters : TSysCharSet) : string;
var i : integer;
begin
  result := '';
  i := 1;
  while (Length(aString) >= i) and
        (aString[i] in Delimiters) do
    i := i + 1;
  while (Length(aString) >= i) and
        (not(aString[i] in Delimiters)) do Begin
    result := result + aString[i];
    i := i + 1;
  end;
  while (Length(aString) >= i) and
        (aString[i] in Delimiters) do
    i := i + 1;
  aString := copy(aString,i,Length(aString));
end;

function TSkypeControl.MessageHook(var Msg: TMessage) : boolean;
var subdata,key : string;
begin
  // processing incoming Messages...
  result := false;
  if (Msg.Msg = WM_COPYDATA) and (HWND(Msg.WParam) = SkypeAPIWindowHandle)
    then Begin
      key := ReceiveData(Msg,subdata);
      DispatchMessage(key,subdata);
      Msg.Result := 1; // Return Code must be different from zero, otherwise
                       // Skype will consider the connection broken.
      result := true;
    end else
  if Msg.Msg = MsgIDAttach
    then case Msg.LParam of
      ATTACH_SUCCESS               : Begin
                                       SkypeAPIWindowHandle := Msg.WParam;
                                       Msg.Result := 1;
                                       FAttached := true;
                                       introduce;
                                       if assigned(FOnAttachSuccess)
                                         then FOnAttachSuccess(self);
                                       result := true;
                                     end;
      ATTACH_PENDING_AUTHORIZATION : Begin
                                       Msg.Result := 1;
                                       if assigned(FOnAttachPending)
                                         then FOnAttachPending(self);
                                       result := true;
                                     end;
      ATTACH_REFUSED               : Begin
                                       Msg.Result := 1;
                                       if assigned(FOnAttachRefused)
                                         then FOnAttachRefused(self);
                                       result := true;
                                     end;
      ATTACH_NOT_AVAILABLE         : Begin
                                       Msg.Result := 1;
                                       if assigned(FOnAPINotAvailable)
                                         then FOnAPINotAvailable(self);
                                       result := true;
                                     end;
      ATTACH_API_AVAILABLE         : Begin
                                       Msg.Result := 1;
                                       if FAutoReAttach
                                         then AttachToSkype;
                                       if assigned(FOnAPIAvailable)
                                         then FOnAPIAvailable(self);
                                       result := true;
                                     end;
    end;
end;

function TSkypeControl.SendData(aString : string) : boolean;
var tmpdata : CopyDataStruct;
    buffer  : array[0..1023] of char;
    i : integer;
begin
  result := true;
  if (Length(aString) > 0) and (Fattached = true)
    then Begin
      for i := 1 to Length(aString) do
        buffer[i-1] := aString[i];
      buffer[Length(aString)] := Chr(0);
      tmpdata.dwData := 0;
      tmpdata.lpData := @buffer;
      tmpdata.cbData := Length(aString)+1;
      if SendMessage( SkypeAPIWindowHandle,
                      WM_COPYDATA,
                      Application.Handle,
                      LPARAM(@tmpdata)) = 0
        then result := false;
    end;
end;

function TSkypeControl.ReceiveData(aMsg : TMessage;
                                   var subdata : string) : string;
var tmpdata : PCopyDataStruct;
    i : integer;
begin
  result  := '';
  subdata := '';
  tmpdata := PCopyDataStruct(aMsg.LParam);
  i := 0;
  while (PChar(tmpdata.lpData)[i] <> #0)  do Begin
    subdata := subdata + PChar(tmpdata.lpData)[i];
    i := i + 1;
  end;
  if assigned(FOnCopyData)
    then FOnCopyData(subdata);
  result := getFirstDelimited(subdata,[' ']);
end;

procedure TSkypeControl.AttachToSkype(Protocol : integer);
begin
  FUsedProtocol := Protocol;
  SendMessage( HWND_BROADCAST, MsgIDDiscover, Application.Handle, 0);
end;

procedure TSkypeControl.introduce;
begin
  // send NAME and PROTOCOL ...
  SendData('NAME ' + FIdentName);
  SendData('PROTOCOL ' + IntToStr(FUsedProtocol));
end;

procedure TSkypeControl.DetachFromSkype;
begin
  SkypeAPIWindowHandle := 0;
  FAttached := false;
end;

procedure TSkypeControl.DispatchMessage(Key, subdata : string);
begin
  if Length(Key) > 0 then
    if (Key = 'ERROR')
      then EvaluateERROR(subdata) else
    if (Key = 'USERSTATUS')
      then EvaluateUSERSTATUS(subdata) else
    if (Key = 'USER')
      then EvaluateUSER(subdata) else
    if (Key = 'NAME')
      then EvaluateNAME(subdata) else
    if (Key = 'PROTOCOL')
      then EvaluatePROTOCOL(subdata) else
    if (Key = 'PONG')
      then EvaluatePONG(subdata) else
    if (Key = 'CALL')
      then EvaluateCALL(subdata) else
    if (Key = 'MESSAGE')
      then EvaluateMESSAGE(subdata) else
    if (Key = 'CONNSTATUS')
      then EvaluateCONNSTATUS(subdata) else
    if (Key = 'CURRENTUSERHANDLE')
      then EvaluateCURRENTUSERHANDLE(subdata) else
    if (Key = 'USERS')
      then EvaluateSEARCHRESULTS(stUsers,subdata) else
    if (Key = 'FRIENDS')
      then EvaluateSEARCHRESULTS(stFriends,subdata) else
    if (Key = 'CALLS')
      then EvaluateSEARCHRESULTS(stCalls,subdata) else
    if (Key = 'MISSEDCALLS')
      then EvaluateSEARCHRESULTS(stMissedCalls,subdata) else
    if (Key = 'MESSAGES')
      then EvaluateSEARCHRESULTS(stMessages,subdata) else
    if (Key = 'MISSEDMESSAGES')
      then EvaluateSEARCHRESULTS(stMissedMessages,subdata) else
    if (Key = 'CHATS')
      then EvaluateSEARCHRESULTS(stChats,subdata) else
    if (Key = 'CHATMESSAGES')
      then EvaluateSEARCHRESULTS(stChatMsgs,subdata) else
    if (Key = 'CALLHISTORYCHANGED')
      then EvaluateCALLHISTORYCHANGED(subdata) else
    if (Key = 'IMHISTORYCHANGED')
      then EvaluateIMHISTORYCHANGED(subdata) else
    if (Key = 'AUDIO_IN')
      then EvaluateAUDIO_IN(subdata) else
    if (Key = 'AUDIO_OUT')
      then EvaluateAUDIO_OUT(subdata) else
    if (Key = 'MUTE')
      then EvaluateMUTE(subdata) else
    if (Key = 'PRIVILEGE')
      then EvaluatePRIVILEGE(subdata) else
    if (Key = 'PROFILE')
      then EvaluatePROFILE(subdata) else
    if (Key = 'SKYPEVERSION')
      then EvaluateSKYPEVERSION(subdata) else
    if (Key = 'CHAT')
      then EvaluateCHAT(subdata) else
    if (Key = 'CHATMESSAGE')
      then EvaluateCHATMESSAGE(subdata);
end;

procedure TSkypeControl.NotSupportedError(errormsg : string;
                                          killrequest : boolean);
begin
  if (RequestPending = true) and
     (killrequest = true)
    then Begin
      RequestError   := true;
      RequestPending := false;
    end;
  if assigned(FOnNotSupportedError)
    then FOnNotSupportedError(errormsg);
end;

procedure TSkypeControl.EvaluateERROR(subdata : string);
var eCode : integer;
Begin
  eCode := StrToIntDef(getFirstDelimited(subdata,[' ']),0);
  if RequestPending
    then Begin
      RequestError   := true;
      RequestPending := false;
    end;
  if (eCode > 0) and (assigned(FOnError) = true)
    then FOnError(eCode,subdata);
end;

procedure TSkypeControl.EvaluateUSERSTATUS(subdata : string);
var userStatus,itr : TSkypeUserStatus;
    status : string;
    fireevent : boolean;
Begin
  userStatus := usUnknown;
  status := getFirstDelimited(subdata,[' ']);
  fireevent := true;
  if Length(status) > 0
    then Begin
      for itr := usUnknown to usSkypeMe do
        if status = USERSTATUS_STR[itr]
          then Begin
            userStatus := itr;
            break;
          end;

      if (RequestPending = true) and
         (RequestID = 'USERSTATUS')
        then Begin
          RequestResult := userStatus;
          RequestPending := false;
          fireevent := FEventOnRequest;
        end;

      // some protocol checkup
      if not(userStatus in READABLE_USERSTATUS[FUsedProtocol])
        then NotSupportedError('Received Userstatus "' + status +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (assigned(FOnUserStatus) = true) and
         (fireevent = true)
        then FOnUserStatus(userStatus);
    end;
end;

procedure TSkypeControl.EvaluateUSER(subdata : string);
var userProperty,itr : TSkypeUserProperty;
    username, prop : string;
    fireevent : boolean;
Begin
  userProperty := upHandle;
  username := getFirstDelimited(subdata,[' ']);
  prop := getFirstDelimited(subdata,[' ']);
  fireevent := true;
  if Length(username) > 0
    then Begin
      if Length(prop) > 0
        then for itr := upHandle to upLastOnlineTimestamp do
          if prop = USERPROPERTY_STR[itr]
            then Begin
              userProperty := itr;
              break;
            end;

      if (RequestPending = true) and
         (RequestID = 'USER ' + username + ' ' + prop)
        then Begin
          RequestResult := subdata;
          RequestPending := false;
          fireevent := FEventOnRequest;
        end;

      // some protocol checkup
      if not(userProperty in READABLE_USERPROPERTY[FUsedProtocol])
        then NotSupportedError('Received user property "' + prop +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (assigned(FOnUserInfo) = true) and
         (fireevent = true)
        then FOnUserInfo(username,userProperty,subdata);
    end;
end;

procedure TSkypeControl.EvaluateNAME(subdata : string);
Begin
  if assigned(FOnName)
    then FOnName(subdata);
end;

procedure TSkypeControl.EvaluatePROTOCOL(subdata : string);
Begin
  FUsedProtocol := StrToIntDef(getFirstDelimited(subdata,[' ']),1);
  if assigned(FOnProtocol)
    then FOnProtocol(FUsedProtocol);
end;

procedure TSkypeControl.EvaluatePONG(subdata : string);
Begin
  if assigned(FOnPong)
    then FOnPong(self);
end;

procedure TSkypeControl.EvaluateCALL(subdata : string);
var callProperty, itr : TSkypeCallProperty;
    prop : string;
    callid : integer;
    fireevent : boolean;
Begin
  callProperty := cpTimestamp;
  callid := StrToIntDef(getFirstDelimited(subdata,[' ']),-1);
  fireevent := true;
  if callid >= 0
    then Begin
      prop := getFirstDelimited(subdata,[' ']);
      for itr := cpTimestamp to cpPSTNStatus do
        if prop = CALLPROPERTY_STR[itr]
          then Begin
            callProperty := itr;
            break;
          end;

      if (RequestPending = true)
        then Begin
           // blGetProperty ...
          if (RequestID = 'CALL ' + IntToStr(callid) + ' ' + prop)
            then Begin
              RequestResult := subdata;
              RequestPending := false;
              fireevent := FEventOnRequest;
            end;
          // blCall ....
          if (RequestID = 'CALL') and
             (callProperty = cpStatus) and
             (subdata = 'ROUTING')
            then Begin
              RequestResult := callid;
              RequestPending := false;
              fireevent := FEventOnRequest;
            end;
        end;

      // some protocol checkup
      if not(callProperty in READABLE_CALLPROPERTY[FUsedProtocol])
        then NotSupportedError('Received call property "' + prop +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (assigned(FOnCallInfo) = true) and
         (fireevent = true)
        then FOnCallInfo(callid,callProperty,subdata);
    end;
end;

procedure TSkypeControl.EvaluateMESSAGE(subdata : string);
var msgProperty, itr : TSkypeMessageProperty;
    prop : string;
    msgid : integer;
    fireevent : boolean;
Begin
  msgProperty := mpTimestamp;
  msgid := StrToIntDef(getFirstDelimited(subdata,[' ']),-1);
  fireevent := true;
  if msgid >= 0
    then Begin
      prop := getFirstDelimited(subdata,[' ']);
      for itr := mpTimestamp to mpBody do
        if prop = MESSAGEPROPERTY_STR[itr]
          then Begin
            msgProperty := itr;
            break;
          end;

      if (RequestPending = true) and
         (RequestID = 'MESSAGE ' + IntToStr(msgid) + ' ' + prop)
        then Begin
          RequestResult := subdata;
          RequestPending := false;
          fireevent := FEventOnRequest;
        end;

      // some protocol checkup
      if not(msgProperty in READABLE_MESSAGEPROPERTY[FUsedProtocol])
        then NotSupportedError('Received message property "' + prop +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (assigned(FOnMessageInfo) = true) and
         (fireevent = true)
        then FOnMessageInfo(msgid,msgProperty,subdata);
    end;
end;

procedure TSkypeControl.EvaluateCONNSTATUS(subdata : string);
var connStatus, itr : TSkypeConnStatus;
    status : string;
    fireevent : boolean;
Begin
  connStatus := csOffline;
  status := getFirstDelimited(subdata,[' ']);
  fireevent := true;
  if Length(status) > 0
    then Begin
      for itr := csOffline to csLoggedOut do
        if status = CONNSTATUS_STR[itr]
          then Begin
            connStatus := itr;
            break;
          end;

      if (RequestPending = true) and
         (RequestID = 'CONNSTATUS')
        then Begin
          RequestResult := connStatus;
          RequestPending := false;
          fireevent := FEventOnRequest;
        end;

      // some protocol checkup
      if not(connStatus in READABLE_CONNSTATUS[FUsedProtocol])
        then NotSupportedError('Received Connstatus "' + status +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (assigned(FOnConnStatus) = true) and
         (fireevent = true)
        then FOnConnStatus(connStatus);
    end;
end;

procedure TSkypeControl.EvaluateCURRENTUSERHANDLE(subdata : string);
var uhand : string;
    fireevent : boolean;
Begin
  fireevent := true;
  uhand := getFirstDelimited(subdata,[' ']);
  if (RequestPending = true) and
     (RequestID = 'CURRENTUSERHANDLE')
    then Begin
      RequestResult := uhand;
      RequestPending := false;
      fireevent := FEventOnRequest;
    end;
  if (Length(uhand) > 0) and (assigned(FOnCurrentUserHandle) = true) and
     (fireevent = true)
    then FOnCurrentUserHandle(uhand);
end;

procedure TSkypeControl.EvaluateSEARCHRESULTS(aType : TSkypeSearchType;
                                              subdata : string);
var tmpstr : string;
    tmplst : TStringList;
    fireevent : boolean;
Begin
  tmplst := TStringList.Create;
  fireevent := true;
  repeat
    tmpstr := getFirstDelimited(subdata,[' ',',']);
    if Length(tmpstr) > 0
      then tmplst.Append(tmpstr);
  until Length(tmpstr) = 0;

  if (RequestPending = true) and
     (RequestID = SEARCHTYPE_STR[aType])
    then Begin
      RequestResult := tmplst.Text;
      RequestPending := false;
      fireevent := FEventOnRequest;
    end;

  // some protocol checkup
  if not (aType in SEARCHABLE_TYPE[FUsedProtocol])
    then NotSupportedError('Search result of type "' +
                           SEARCHTYPE_STR[aType] +
                           '" should not have been received (Protocol '+
                           IntToStr(FUsedProtocol)+')');

  if (assigned(FOnSearchResult) = true) and
     (fireevent = true)
    then FOnSearchResult(aType,tmplst)
    else tmplst.Free;
end;

procedure TSkypeControl.EvaluateCALLHISTORYCHANGED(subdata : string);
Begin
  if assigned(FOnCallHistoryChanged)
    then FOnCallHistoryChanged(self);
end;

procedure TSkypeControl.EvaluateIMHISTORYCHANGED(subdata : string);
Begin
  if assigned(FOnIMHistoryChanged)
    then FOnIMHistoryChanged(self);
end;

procedure TSkypeControl.EvaluateAUDIO_IN(subdata : string);
var tmpdev : string;
    fireevent : boolean;
Begin
  fireevent := true;
  tmpdev := getFirstDelimited(subdata,[' ']);
  if (RequestPending = true) and
     (RequestID = 'AUDIO_IN')
    then Begin
      RequestResult := tmpdev;
      RequestPending := false;
      fireevent := FEventOnRequest;
    end;
  if (assigned(FOnAudioInInfo) = true) and
     (fireevent = true)
    then FOnAudioInInfo(tmpdev);
end;

procedure TSkypeControl.EvaluateAUDIO_OUT(subdata : string);
var tmpdev : string;
    fireevent : boolean;
Begin
  fireevent := true;
  tmpdev := getFirstDelimited(subdata,[' ']);
  if (RequestPending = true) and
     (RequestID = 'AUDIO_OUT')
    then Begin
      RequestResult := tmpdev;
      RequestPending := false;
      fireevent := FEventOnRequest;
    end;
  if (assigned(FOnAudioOutInfo) = true) and
     (fireevent = true)
    then FOnAudioOutInfo(tmpdev);
end;

procedure TSkypeControl.EvaluateMUTE(subdata : string);
var muted : boolean;
    mstr : string;
    fireevent : boolean;
begin
  fireevent := true;
  muted := false;
  mstr :=  getFirstDelimited(subdata,[' ']);
  if mstr = 'ON'
    then muted := true;
  if (RequestPending = true) and
     (RequestID = 'MUTE')
    then Begin
      RequestResult := muted;
      RequestPending := false;
      fireevent := FEventOnRequest;
    end;

  if (Length(mstr) > 0) and (assigned(FOnMuteInfo) = true) and
     (fireevent = true)
    then FOnMuteInfo(muted);
end;

procedure TSkypeControl.EvaluatePRIVILEGE(subdata : string);
var granted : boolean;
    privtype, itr : TSkypePrivilege;
    priv, value : string;
    fireevent : boolean;
begin
  fireevent := true;
  granted  := false;
  privtype := spSkypeOut;
  priv  := getFirstDelimited(subdata,[' ']);
  value := getFirstDelimited(subdata,[' ']);
  if Length(priv) > 0
    then Begin
      for itr := spSkypeOut to spSkypeOut do
        if priv = PRIVILEGE_STR[itr]
          then Begin
            privtype := itr;
            break;
          end;
      if value = 'TRUE'
        then granted := true;

      if (RequestPending = true) and
         (RequestID = 'PRIVILEGE')
        then Begin
          RequestResult := granted;
          RequestPending := false;
          fireevent := FEventOnRequest;
        end;

      // some protocol checkup
      if not(privtype in READABLE_PRIVILEGE[FUsedProtocol])
        then NotSupportedError('Received privilege info "' + priv +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (Length(value) > 0) and (assigned(FOnPrivilegeInfo) = true) and
         (fireevent = true)
        then FOnPrivilegeInfo(privtype,granted);
    end;
end;

procedure TSkypeControl.EvaluatePROFILE(subdata : string);
var proptype, itr : TSkypeProfile;
    prop, value : string;
    fireevent : boolean;
begin
  fireevent := true;
  proptype := spfPSTNBalance;
  prop  := getFirstDelimited(subdata,[' ']);
  //value := getFirstDelimited(subdata,[' ']);
  value := subdata;
  if Length(prop) > 0
    then Begin
      for itr := spfPSTNBalance to spfPSTNBalanceCurrency do
        if prop = PROFILE_STR[itr]
          then Begin
            proptype := itr;
            break;
          end;

      if (RequestPending = true) and
         (RequestID = 'PROFILE')
        then Begin
          RequestResult := value;
          RequestPending := false;
          fireevent := FEventOnRequest;
        end;

      // some protocol checkup
      if not(proptype in READABLE_PROFILE[FUsedProtocol])
        then NotSupportedError('Received profile info "' + prop +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (Length(value) > 0) and (assigned(FOnProfileInfo) = true) and
         (fireevent = true)
        then FOnProfileInfo(proptype,value);
    end;
end;

procedure TSkypeControl.EvaluateSKYPEVERSION(subdata : string);
var version : string;
    fireevent : boolean;
begin
  fireevent := true;
  version := getFirstDelimited(subdata,[' ']);
  if (RequestPending = true) and
     (RequestID = 'SKYPEVERSION')
    then Begin
      RequestResult := version;
      RequestPending := false;
      fireevent := FEventOnRequest;
    end;
  if (Length(version) > 0) and (assigned(FOnVersionInfo) = true) and
     (fireevent = true)
    then FOnVersionInfo(version);
end;

procedure TSkypeControl.EvaluateCHAT(subdata : string);
var chatProperty,itr : TSkypeChatProperty;
    chatname,prop : string;
    fireevent : boolean;
Begin
  chatProperty := chName;
  chatname := getFirstDelimited(subdata,[' ']);
  fireevent := true;
  if Length(chatname) > 0
    then Begin
      prop := getFirstDelimited(subdata,[' ']);
      for itr := chName to chFriendlyname do
        if prop = CHATPROPERTY_STR[itr]
          then Begin
            chatProperty := itr;
            break;
          end;

      if (RequestPending = true) and
         (RequestID = 'CHAT ' + chatname + ' ' + prop)
        then Begin
          RequestResult := subdata;
          RequestPending := false;
          fireevent := FEventOnRequest;
        end;

      // some protocol checkup
      if not(chatProperty in READABLE_CHATPROPERTY[FUsedProtocol])
        then NotSupportedError('Received chat property "' + prop +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (assigned(FOnChatInfo) = true) and
         (fireevent = true)
        then FOnChatInfo(chatname,chatProperty,subdata);
    end;
end;

procedure TSkypeControl.EvaluateCHATMESSAGE(subdata : string);
var chatMsgProperty, itr : TSkypeChatMsgProperty;
    prop : string;
    msgid : integer;
    fireevent : boolean;
Begin
  chatMsgProperty := cmChatname;
  msgid := StrToIntDef(getFirstDelimited(subdata,[' ']),-1);
  fireevent := true;
  if msgid >= 0
    then Begin
      prop := getFirstDelimited(subdata,[' ']);
      for itr := cmChatname to cmSeen do
        if prop = CHATMSGPROPERTY_STR[itr]
          then Begin
            chatMsgProperty := itr;
            break;
          end;

      if (RequestPending = true) and
         (RequestID = 'CHATMESSAGE ' + IntToStr(msgid) + ' ' + prop)
        then Begin
          RequestResult := subdata;
          RequestPending := false;
          fireevent := FEventOnRequest;
        end;

      // some protocol checkup
      if not(chatMsgProperty in READABLE_CHATMSGPROPERTY[FUsedProtocol])
        then NotSupportedError('Received chatmessage property "' + prop +
                               '" should not have been reveived (Protocol '+
                               IntToStr(FUsedProtocol) +')' );

      if (assigned(FOnChatMsgInfo) = true) and
         (fireevent = true)
        then FOnChatMsgInfo(msgid,chatMsgProperty,subdata);
    end;
end;

procedure TSkypeControl.Search(aType : TSkypeSearchType; Target : string);
begin
  if aType in SEARCHABLE_TYPE[FUsedProtocol]
    then SendData('SEARCH ' + SEARCHTYPE_STR[aType] + ' ' + Target)
    else NotSupportedError('Searchtype "' +
                           SEARCHTYPE_STR[aType] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')', true );
end;

procedure TSkypeControl.GetProperty(Username     : string;
                                    UserProperty : TSkypeUserProperty);
begin
  if UserProperty in READABLE_USERPROPERTY[FUsedProtocol]
    then SendData('GET USER ' + Username + ' ' + USERPROPERTY_STR[UserProperty])
    else NotSupportedError('User property "' +
                           USERPROPERTY_STR[UserProperty] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')', true );
end;

procedure TSkypeControl.GetProperty(CallID       : integer;
                                    CallProperty : TSkypeCallProperty);
begin
  if CallProperty in READABLE_CALLPROPERTY[FUsedProtocol]
    then SendData('GET CALL ' + IntToStr(CallID) +
                  ' ' + CALLPROPERTY_STR[CallProperty])
    else NotSupportedError('Call property "' +
                           CALLPROPERTY_STR[CallProperty] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')', true );
end;

procedure TSkypeControl.GetProperty(MessageID       : integer;
                                    MessageProperty : TSkypeMessageProperty);
begin
  if MessageProperty in READABLE_MESSAGEPROPERTY[FUsedProtocol]
    then SendData('GET MESSAGE ' + IntToStr(MessageID) +
                  ' ' + MESSAGEPROPERTY_STR[MessageProperty])
    else NotSupportedError('Message property "' +
                           MESSAGEPROPERTY_STR[MessageProperty] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')', true );
end;

procedure TSkypeControl.GetProperty(Chatname     : string;
                                    ChatProperty : TSkypeChatProperty);
begin
  if ChatProperty in READABLE_CHATPROPERTY[FUsedProtocol]
    then SendData('GET CHAT ' + Chatname + ' ' + CHATPROPERTY_STR[ChatProperty])
    else NotSupportedError('Chat property "' +
                           CHATPROPERTY_STR[ChatProperty] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')', true );
end;

procedure TSkypeControl.GetProperty(ChatMsgID       : integer;
                                    ChatMsgProperty : TSkypeChatMsgProperty);
begin
  if ChatMsgProperty in READABLE_CHATMSGPROPERTY[FUsedProtocol]
    then SendData('GET CHATMESSAGE ' + InttoStr(ChatMsgID) +
                  ' ' + CHATMSGPROPERTY_STR[ChatMsgProperty])
    else NotSupportedError('Chatmessage property "' +
                           CHATMSGPROPERTY_STR[ChatMsgProperty] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')', true );
end;

procedure TSkypeControl.GetUserStatus;
begin
  SendData('GET USERSTATUS');
end;

procedure TSkypeControl.GetConnStatus;
begin
  SendData('GET CONNSTATUS');
end;

procedure TSkypeControl.GetAudioInInfo;
begin
  SendData('GET AUDIO_IN');
end;

procedure TSkypeControl.GetAudioOutInfo;
begin
  SendData('GET AUDIO_OUT');
end;

procedure TSkypeControl.GetCurrentUserHandle;
begin
  SendData('GET CURRENTUSERHANDLE');
end;

procedure TSkypeControl.GetMuteInfo;
begin
  SendData('GET MUTE');
end;

procedure TSkypeControl.GetPrivilegeInfo(priv : TSkypePrivilege);
begin
  if priv in READABLE_PRIVILEGE[FUsedProtocol]
    then SendData('GET PRIVILEGE ' + PRIVILEGE_STR[priv])
    else NotSupportedError('Privilege "' +
                           PRIVILEGE_STR[priv] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')' );
end;

procedure TSkypeControl.GetProfileInfo(prop : TSkypeProfile);
begin
  if prop in READABLE_PROFILE[FUsedProtocol]
    then SendData('GET PROFILE ' + PROFILE_STR[prop])
    else NotSupportedError('Profile "' +
                           PROFILE_STR[prop] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')' );
end;

procedure TSkypeControl.GetSkypeVersion;
begin
  SendData('GET SKYPEVERSION');
end;

procedure TSkypeControl.SetProperty(CallID : integer;
                                    CallProperty : TSkypeCallProperty;
                                    Value : string);
begin
  if CallProperty in WRITEABLE_CALLPROPERTY[FUsedProtocol]
    then SendData('SET CALL ' + IntToStr(CallID) + ' ' +
                  CALLPROPERTY_STR[CallProperty] + ' ' + Value)
    else NotSupportedError('Setting call property "' +
                           CALLPROPERTY_STR[CallProperty] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')' );
end;

procedure TSkypeControl.SetProperty(MessageID : integer;
                                    MessageProperty : TSkypeMessageProperty);
begin
  if MessageProperty in WRITEABLE_MESSAGEPROPERTY[FUsedProtocol]
    then SendData('SET MESSAGE ' + IntToStr(MessageID) + ' ' +
                  MESSAGEPROPERTY_STR[MessageProperty])
    else NotSupportedError('Setting message property "' +
                           MESSAGEPROPERTY_STR[MessageProperty] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')' );
end;

procedure TSkypeControl.SetProperty(ChatMsgID : integer;
                                    ChatMsgProperty : TSkypeChatMsgProperty);
begin
  if ChatMsgProperty in WRITEABLE_CHATMSGPROPERTY[FUsedProtocol]
    then SendData('SET CHATMESSAGE ' + IntToStr(ChatMsgID) + ' ' +
                  CHATMSGPROPERTY_STR[ChatMsgProperty])
    else NotSupportedError('Setting chatmessage property "' +
                           CHATMSGPROPERTY_STR[ChatMsgProperty] +
                           '" not supported (Protocol '+
                           IntToStr(FUsedProtocol)+')' );
end;

procedure TSkypeControl.SetUserStatus(Status : TSkypeUserStatus);
begin
  if Status in WRITEABLE_USERSTATUS[FUsedProtocol]
    then SendData('SET USERSTATUS ' + USERSTATUS_STR[Status])
    else NotSupportedError('Setting Userstatus "' +
                           USERSTATUS_STR[Status] +
                           '" not possible (Protocol ' +
                           IntToStr(FUsedProtocol)+')');
end;

procedure TSkypeControl.SetMute(Muted : boolean);
begin
  if Muted
    then SendData('SET MUTE ON')
    else SendData('SET MUTE OFF');
end;

procedure TSkypeControl.Call(Target : string);
begin
  SendData('CALL ' + Target);
end;

procedure TSkypeControl.SendMsg(Username : string; aText : string);
begin
  SendData('MESSAGE ' + Username + ' ' + aText);
end;

procedure TSkypeControl.AddContactDialog;
begin
  SendData('OPEN ADDAFRIEND');
end;

procedure TSkypeControl.IMDialog(username : string; aText : string);
begin
  SendData('OPEN IM ' + username + ' ' + aText);
end;

procedure TSkypeControl.OpenChat(users : string);
begin
  SendData('OPEN CHAT ' + users);
end;

procedure TSkypeControl.FocusSkype;
begin
  SendData('FOCUS');
end;

procedure TSkypeControl.PingSkype;
begin
  SendData('PING');
end;

//------------------------------------------------------------------------------
// blocking Methods..

procedure TSkypeControl.ResetRequest;
begin
  RequestPending := false;
  RequestID      := '';
  RequestResult  := '';
  RequestError   := false;
end;

function TSkypeControl.blSearch(aType : TSkypeSearchType;
                                Target : string) : TStringList;
begin
  result := TStringList.Create;
  RequestID := SEARCHTYPE_STR[aType];
  RequestPending := true;
  Search(aType,Target);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result.Text := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetProperty(Username     : string;
                                     UserProperty : TSkypeUserProperty): string;
Begin
  result := '';
  RequestID := 'USER ' + Username + ' ' + USERPROPERTY_STR[UserProperty];
  RequestPending := true;
  GetProperty(username,UserProperty);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetProperty(CallID       : integer;
                                     CallProperty : TSkypeCallProperty): string;
Begin
  result := '';
  RequestID := 'CALL ' + IntToStr(CallID) +
               ' ' + CALLPROPERTY_STR[CallProperty];
  RequestPending := true;
  GetProperty(CallID,CallProperty);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetProperty(MessageID : integer;
                               MessageProperty : TSkypeMessageProperty): string;
Begin
  result := '';
  RequestID := 'MESSAGE ' + IntToStr(MessageID) +
               ' ' + MESSAGEPROPERTY_STR[MessageProperty];
  RequestPending := true;
  GetProperty(MessageID,MessageProperty);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetProperty(Chatname     : string;
                                     ChatProperty : TSkypeChatProperty): string;
Begin
  result := '';
  RequestID := 'CHAT ' + Chatname + ' ' + CHATPROPERTY_STR[ChatProperty];
  RequestPending := true;
  GetProperty(Chatname,ChatProperty);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetProperty(ChatMsgID : integer;
                               ChatMsgProperty : TSkypeChatMsgProperty): string;
Begin
  result := '';
  RequestID := 'CHATMESSAGE ' + IntToStr(ChatMsgID) +
               ' ' + CHATMSGPROPERTY_STR[ChatMsgProperty];
  RequestPending := true;
  GetProperty(ChatMsgID,ChatMsgProperty);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetUserStatus : TSkypeUserStatus;
begin
  result := usUnknown;
  RequestID := 'USERSTATUS';
  RequestPending := true;
  GetUserstatus;
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetConnStatus : TSkypeConnStatus;
begin
  result := csOffline;
  RequestID := 'CONNSTATUS';
  RequestPending := true;
  GetConnstatus;
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetAudioInInfo  : string;
begin
  result := '';
  RequestID := 'AUDIO_IN';
  RequestPending := true;
  GetAudioInInfo;
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetAudioOutInfo : string;
begin
  result := '';
  RequestID := 'AUDIO_OUT';
  RequestPending := true;
  GetAudioOutInfo;
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetCurrentUserHandle : string;
begin
  result := '';
  RequestID := 'CURRENTUSERHANDLE';
  RequestPending := true;
  GetCurrentUserHandle;
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetMuteInfo : boolean;
begin
  result := false;
  RequestID := 'MUTE';
  RequestPending := true;
  GetMuteInfo;
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetPrivilegeInfo(priv : TSkypePrivilege) : boolean;
begin
  result := false;
  RequestID := 'PRIVILEGE';
  RequestPending := true;
  GetPrivilegeInfo(priv);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetProfileInfo(prop : TSkypeProfile) : string;
begin
  result := '';
  RequestID := 'PROFILE';
  RequestPending := true;
  GetProfileInfo(prop);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blGetSkypeVersion : string;
begin
  result := '';
  RequestID := 'SKYPEVERSION';
  RequestPending := true;
  GetSkypeVersion;
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;

function TSkypeControl.blCall(Target : string) : integer;
begin
  result := -1;
  RequestID := 'CALL';
  RequestPending := true;
  Call(Target);
  while RequestPending do Begin
    sleep(1);
    Application.ProcessMessages;
  end;
  if not RequestError
    then result := RequestResult;
  ResetRequest;
end;


end.
